#pragma once

#include <iostream>

#include "kconfig.h"

#if _DEBUG

extern const char *FuncIdents[];
extern const char *ObjectIdents[];
extern const char *MethodIdents[];

#endif

enum KTokenType
{
	TOK_NULL,      // end of token list

	TOK_EOS,       // end of statement

	TOK_LITINT,    // literal int
	TOK_LITREAL,   // literal real
	TOK_LITSTRING, // literal string
	TOK_VAR,       // variable identifier

	TOK_FUNC,      // built-in function identifier
	TOK_OBJECT,    // object identifier
	TOK_METHOD,    // method identifier

	TOK_INT,       // keyword 'int'
	TOK_REAL,      // keyword 'real'
	TOK_STRING,    // keyword 'string'
	TOK_IF,        // keyword 'if'
	TOK_AT,        // keyword 'at'

	TOK_UPLUS,     // unary +
	TOK_UMINUS,    // unary -
	TOK_ADD,       // +
	TOK_SUB,       // -
	TOK_MUL,       // *
	TOK_DIV,       // /
	TOK_IDIV,      // backslash
	TOK_MOD,       // %
	TOK_POW,       // ^
	TOK_EQU,       // =
	TOK_NEQ,       // <>
	TOK_LT,        // <
	TOK_LTE,       // <=
	TOK_GT,        // >
	TOK_GTE,       // >=
	TOK_CAT,       // &
	TOK_AND,       // keyword 'and'
	TOK_OR,        // keyword 'or'
	TOK_XOR,       // keyword 'xor'
	TOK_NOT,       // keyword 'not'
	TOK_CINT,      // @
	TOK_CREAL,     // #
	TOK_COMMA,     // ,
	TOK_COLON,     // :
	TOK_SELECTOR,  // .

	TOK_LPAR,      // (
	TOK_RPAR       // )
};

struct Token
{
	KTokenType type;
	union
	{
		k_int  v_int;
		k_real v_real;
	};
	k_string   v_string;

	k_uint line;

	bool isBinary;
	bool canPrecedeUnary;
	int  precedence;

	Token(k_int v) : v_int(v) {}

	Token(KTokenType t) : type(t) {}

	Token(KTokenType t, k_string v) : type(t), v_string(v) {}

#if _DEBUG
	friend std::ostream& operator <<(std::ostream & os, const Token& tok)
	{
		switch (tok.type)
		{
		case TOK_NULL:
			os << "~NULL";
			break;
		case TOK_EOS:
			os << "~EOS";
			break;
		case TOK_LITINT:
			os << tok.v_int;
			break;
		case TOK_LITREAL:
			os << tok.v_real;
			break;
		case TOK_LITSTRING:
			os << "\"" << tok.v_string << "\"";
			break;
		case TOK_VAR:
			os << tok.v_string;
			break;
		case TOK_FUNC:
			os << FuncIdents[tok.v_int];
			break;
		case TOK_OBJECT:
			os << ObjectIdents[tok.v_int];
			break;
		case TOK_METHOD:
			os << MethodIdents[tok.v_int];
			break;
		case TOK_INT:
			os << "int";
			break;
		case TOK_REAL:
			os << "real";
			break;
		case TOK_STRING:
			os << "string";
			break;
		case TOK_IF:
			os << "if";
			break;
		case TOK_AT:
			os << "at";
			break;
		case TOK_UPLUS:
			os << "u+";
			break;
		case TOK_UMINUS:
			os << "u-";
			break;
		case TOK_ADD:
			os << "+";
			break;
		case TOK_SUB:
			os << "-";
			break;
		case TOK_MUL:
			os << "*";
			break;
		case TOK_DIV:
			os << "/";
			break;
		case TOK_IDIV:
			os << "\\";
			break;
		case TOK_MOD:
			os << "%";
			break;
		case TOK_POW:
			os << "^";
			break;
		case TOK_EQU:
			os << "=";
			break;
		case TOK_NEQ:
			os << "<>";
			break;
		case TOK_LT:
			os << "<";
			break;
		case TOK_LTE:
			os << "<=";
			break;
		case TOK_GT:
			os << ">";
			break;
		case TOK_GTE:
			os << ">=";
			break;
		case TOK_CAT:
			os << "&";
			break;
		case TOK_AND:
			os << "and";
			break;
		case TOK_OR:
			os << "or";
			break;
		case TOK_XOR:
			os << "xor";
			break;
		case TOK_NOT:
			os << "not";
			break;
		case TOK_CINT:
			os << "@";
			break;
		case TOK_CREAL:
			os << "#";
			break;
		case TOK_COMMA:
			os << ",";
			break;
		case TOK_COLON:
			os << ":";
			break;
		case TOK_SELECTOR:
			os << ".";
			break;
		case TOK_LPAR:
			os << "(";
			break;
		case TOK_RPAR:
			os << ")";
			break;
		}
		return os;
	}
#endif
};
